/*
 * Copyright (C) 2012 ARM Ltd.
 *
 * This program is free software; you can redistribute it and/or modify
 * it under the terms of the GNU General Public License version 2 as
 * published by the Free Software Foundation.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with this program.  If not, see <http://www.gnu.org/licenses/>.
 */
#ifndef ASM_SMP_H
#define ASM_SMP_H

/* Values for secondary_data.status */
#define CPU_STUCK_REASON_SHIFT		(8)
#define CPU_BOOT_STATUS_MASK		((ULL(1) << CPU_STUCK_REASON_SHIFT) - 1)

#define CPU_MMU_OFF             (-1)
#define CPU_BOOT_SUCCESS        (0)
/* 当 cpu die, 并且支持 hutplug cpu 时, ops->cpu_die 调用之前设置 cpu_kill */
#define CPU_KILL_ME             (1)
/* 当 cpu 不能通过 cpu_die 优雅的 die, 并且在内核中循环时设置 */
#define CPU_STUCK_IN_KERNEL     (2)
/* 从核 cpu 检测到致命的系统错误, 系统崩溃时设置 */
#define CPU_PANIC_KERNEL        (3)

#define CPU_STUCK_REASON_52_BIT_VA	(ULL(1) << CPU_STUCK_REASON_SHIFT)
#define CPU_STUCK_REASON_NO_GRAN	(ULL(2) << CPU_STUCK_REASON_SHIFT)

#ifndef __ASSEMBLY__

#include <seminix/percpu.h>
#include <asm/smp_plat.h>
#include <asm/ptrace.h>
#include <asm/barrier.h>

struct task_struct;
struct cpumask;

DECLARE_PER_CPU_READ_MOSTLY(int, cpu_number);

/*
 * We don't use this_cpu_read(cpu_number) as that has implicit writes to
 * preempt_count, and associated (compiler) barriers, that we'd like to avoid
 * the expense of. If we're preemptible, the value can be stale at use anyway.
 * And we can't use this_cpu_ptr() either, as that winds up recursing back
 * here under CONFIG_DEBUG_PREEMPT=y.
 */
#define raw_smp_processor_id() (*raw_cpu_ptr(&cpu_number))

/*
 * Called from C code, this handles an IPI.
 */
extern void handle_IPI(int ipinr, struct pt_regs *regs);

/*
 * 其中前期调用, 用于发现一组可能的 cpu, 并确定他们的 smp 操作
 */
extern void smp_init_cpus(void);

/*
 * 提供 func 去升起对应 cpu 的 ipi 中断, 通过 cpumask 指定需要升起中断的 cpu.
 */
extern void set_smp_cross_call(void (*)(const struct cpumask *, int));

/*
 * 从平台特定的汇编代码调用, 这是从核 cpu 的 C 入口点.
 */
asmlinkage void secondary_start_kernel(void);

/*
 * 启动从核 cpu 的初始化数据
 *
 * @stack  - 从核 cpu 的 stack(sp)
 * @status - 从核 cpu 返回来的启动结果
 */
struct secondary_data {
    void *stack;
    struct task_struct *task;
    long status;
};

extern struct secondary_data secondary_data;
extern long __early_cpu_boot_status;
/* 从核的汇编入口, cpu 启动操作调用 */
extern asmlinkage void secondary_entry(void);

extern void arch_send_call_function_single_ipi(int cpu);
extern void arch_send_call_function_ipi_mask(const struct cpumask *mask);

extern void cpu_die_early(void);

static inline void cpu_park_loop(void)
{
    for (;;) {
        wfe();
        wfi();
    }
}

static inline void update_cpu_boot_status(int val)
{
    WRITE_ONCE(secondary_data.status, val);
    /* Ensure the visibility of the status update */
    dsb(ishst);
}

/*
 * 当从核 cpu 检测到严重的配置不匹配, 这将导致内核 panic.
 * 更新 cpu 的启动状态并叫停该 cpu
 */
static inline void cpu_panic_kernel(void)
{
    update_cpu_boot_status(CPU_PANIC_KERNEL);
    cpu_park_loop();
}

#endif /* !__ASSEMBLY__ */
#endif /* !ASM_SMP_H */
